package pl.shenlon.persistence;

import java.lang.reflect.InvocationTargetException;
import java.util.List;

import org.jdom2.Element;

import pl.shenlon.ga.BehaviourPattern;
import pl.shenlon.ga.Population;
import pl.shenlon.ga.SimpleSpecimen;
import pl.shenlon.ga.Specimen;
import pl.shenlon.ga.StatefulChromosome;
import pl.shenlon.ga.StatefulSpecimen;
import pl.shenlon.ga.evaluation.PopulationEvaluator;
import pl.shenlon.ga.selection.PopulationSpecimenSelector;
import pl.shenlon.poker.game.GameState;

public class PopulationPersistance {
	public static Element persist(Population population) {
		Element element = new Element("population");
		element.addContent(new Element("size").setText(String
				.valueOf(population.getPopulationSize())));
		element.addContent(new Element("mutationRate").setText(String
				.valueOf(population.getMutationRate())));
		element.addContent(new Element("crossoverRate").setText(String
				.valueOf(population.getCrossoverRate())));
		element.addContent(new Element("elitarismRate").setText(String
				.valueOf(population.getElitarismRate())));
		element.addContent(new Element("survivalRate").setText(String
				.valueOf(population.getSurvivalRate())));
		element.addContent(new Element("cash").setText(String
				.valueOf(population.getCash())));
		element.addContent(new Element("specimenClass").setText(population
				.getSpecimenClass().getCanonicalName()));
		element.addContent(new Element("evaluatorClass").setText(population
				.getEvaluator().getClass().getCanonicalName()));
		element.addContent(new Element("selectorClass").setText(population
				.getSelector().getClass().getCanonicalName()));
		Element specimens = new Element("specimens");
		if (population.getSpecimenClass().equals(SimpleSpecimen.class)) {
			for (Specimen specimen : population.getPopulation()) {
				specimens
						.addContent(persistSimpleSpecimen((SimpleSpecimen) specimen));
			}
		} else if (population.getSpecimenClass().equals(StatefulSpecimen.class)) {
			for (Specimen specimen : population.getPopulation()) {
				specimens
						.addContent(persistStatefulSpecimen((StatefulSpecimen) specimen));
			}
		}
		element.addContent(specimens);
		return element;
	}

	public static Element persistBehaviourPattern(BehaviourPattern pattern) {
		Element element = new Element("pattern");
		element.addContent(new Element("bluff").setText(String.valueOf(pattern
				.getBluffChance())));
		element.addContent(new Element("call").setText(String.valueOf(pattern
				.getCallChance())));
		element.addContent(new Element("check").setText(String.valueOf(pattern
				.getCheckChance())));
		element.addContent(new Element("fold").setText(String.valueOf(pattern
				.getFoldChance())));
		element.addContent(new Element("raise").setText(String.valueOf(pattern
				.getRaiseChance())));
		return element;
	}

	public static Element persistGameState(GameState gameState) {
		Element element = new Element("gameState");
		element.addContent(new Element("round").setText(gameState.getRound()));
		element.addContent(new Element("winChance").setText(gameState
				.getWinChanceTerm()));
		element.addContent(new Element("strengthenChance").setText(gameState
				.getStrengthenChanceTerm()));
		element.addContent(new Element("betProfitable").setText(String
				.valueOf(gameState.isBetProfitable())));
		return element;
	}

	public static Element persistSimpleSpecimen(SimpleSpecimen specimen) {
		Element element = new Element("specimen");
		element.addContent(persistBehaviourPattern(specimen.getGenom()
				.getBehaviourPattern(GameState.ANY)));
		element.addContent(new Element("score").setText(String.valueOf(specimen
				.getScore())));
		return element;
	}

	public static Element persistStatefulSpecimen(StatefulSpecimen specimen) {
		Element element = new Element("specimen");
		element.addContent(new Element("score").setText(String.valueOf(specimen
				.getScore())));
		for (GameState gameState : GameState.POSSIBLE_GAME_STATES) {
			Element gameStateElement = persistGameState(gameState);
			gameStateElement.addContent(persistBehaviourPattern(specimen
					.getGenom().getBehaviourPattern(gameState)));
			element.addContent(gameStateElement);
		}
		return element;
	}

	public static BehaviourPattern restoreBehaviourPattern(Element element) {
		int[] genom = new int[5];
		genom[BehaviourPattern.POSITION_BLUFF] = Integer.valueOf(element
				.getChildText("bluff"));
		genom[BehaviourPattern.POSITION_CALL] = Integer.valueOf(element
				.getChildText("call"));
		genom[BehaviourPattern.POSITION_CHECK] = Integer.valueOf(element
				.getChildText("check"));
		genom[BehaviourPattern.POSITION_FOLD] = Integer.valueOf(element
				.getChildText("fold"));
		genom[BehaviourPattern.POSITION_RAISE] = Integer.valueOf(element
				.getChildText("raise"));

		return new BehaviourPattern(genom);
	}

	public static GameState restoreGameState(Element element) {
		return new GameState(element.getChildText("winChance"),
				element.getChildText("strengthenChance"),
				element.getChildText("round"), Boolean.valueOf(element
						.getChildText("betProfitable")));
	}

	@SuppressWarnings("unchecked")
	public static Population<?> restorePopulation(Element element) {
		Class<?> specimenClass = null, selectorClass = null, evaluatorClass = null;
		int size = Integer.valueOf(element.getChildText("size"));
		try {
			specimenClass = Class
					.forName(element.getChildText("specimenClass"));
			selectorClass = Class
					.forName(element.getChildText("selectorClass"));
			evaluatorClass = Class.forName(element
					.getChildText("evaluatorClass"));
		} catch (Exception e) {
			e.printStackTrace();
		}
		Population population = new Population(size, specimenClass);

		population.setCash(Integer.valueOf(element.getChildText("cash")));
		population.setCrossoverRate(Double.valueOf(element
				.getChildText("crossoverRate")));
		population.setMutationRate(Double.valueOf(element
				.getChildText("mutationRate")));
		population.setSurvivalRate(Double.valueOf(element
				.getChildText("survivalRate")));
		population.setElitarismRate(Integer.valueOf(element
				.getChildText("elitarismRate")));
		PopulationSpecimenSelector selector = null;
		PopulationEvaluator evaluator = null;

		try {
			selector = (PopulationSpecimenSelector) selectorClass
					.getDeclaredConstructor(Population.class).newInstance(
							population);
			evaluator = (PopulationEvaluator) evaluatorClass
					.getDeclaredConstructor(Population.class).newInstance(
							population);
		} catch (InstantiationException | IllegalAccessException
				| IllegalArgumentException | InvocationTargetException
				| NoSuchMethodException | SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		population.setSelector(selector);
		population.setEvaluator(evaluator);
		List<Element> specimensList = element.getChild("specimens")
				.getChildren("specimen");
		if (specimenClass.equals(SimpleSpecimen.class)) {
			for (int i = 0; i < size; i++) {
				Element specimenElement = specimensList.get(i);

				SimpleSpecimen specimen = new SimpleSpecimen(
						restoreBehaviourPattern(specimenElement
								.getChild("pattern")));
				specimen.setScore(Integer.valueOf(specimenElement
						.getChildText("score")));
				population.setSpecimen(i, specimen);

			}
		} else if (specimenClass.equals(StatefulSpecimen.class)) {
			for (int i = 0; i < size; i++) {
				Element specimenElement = specimensList.get(i);
				StatefulChromosome genom = new StatefulChromosome();
				for (Element gameStateElement : specimenElement
						.getChildren("gameState")) {
					GameState gameState = restoreGameState(gameStateElement);
					BehaviourPattern pattern = restoreBehaviourPattern(gameStateElement
							.getChild("pattern"));
					genom.setBehaviourPattern(gameState, pattern);
				}
				StatefulSpecimen specimen = new StatefulSpecimen(genom);
				specimen.setScore(Integer.valueOf(specimenElement
						.getChildText("score")));
				// System.out.println(specimen.getScore());
				population.setSpecimen(i, specimen);
			}
		}

		return population;
	}
}